package com.hwtx.form.domain.meta;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.hwtx.form.domain.FormConstants;
import com.hwtx.form.domain.listener.DataPermValueChangeEvent;
import com.hwtx.form.domain.service.ResourceService;
import com.hwtx.form.persistence.entity.SysRoleDataPermPro;
import com.hwtx.form.persistence.mapper.SysRoleDataPermProMapper;
import com.hwtx.form.util.JacksonUtil;
import io.geekidea.boot.system.entity.RoleDataResource;
import io.geekidea.boot.system.mapper.RoleDataResourceMapper;
import io.geekidea.boot.system.mapper.SysMenuMapper;
import io.geekidea.boot.system.vo.SysMenuTreeVo;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class RacMeta {
    private final Cache<String, DataPerms> cache = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.DAYS).build();
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private SysRoleDataPermProMapper sysRoleDataPermProMapper;
    @Resource
    private RoleDataResourceMapper roleDataResourceMapper;
    @Resource
    private ResourceService resourceService;

    @EventListener
    public void dataPermsChangeListener(DataPermValueChangeEvent event) {
        log.info("RacMeta dataPermValueChange event, resourceId = {}", event.getResourceId());
        resourceService.getResourceUrlMapping(List.of(event.getResourceId())).forEach(resource -> {
            if (StringUtils.isNotEmpty(resource.getUrl())) {
                cache.invalidate(resource.getUrl());
            }
        });
    }

    public DataPerms getDataPermsByFunPerm(List<Long> roleIds, String url) {
        try {
            return cache.get(url, () -> {
                DataPerms dataPerms = new DataPerms();
                List<SysRoleDataPermPro> roleForDataPermPros = Lists.newArrayList();
                List<Long> matchedRoleIds = Lists.newArrayList();
                roleIds.forEach(roleId -> {
                    Set<Long> resourceIds = sysMenuMapper.getMenusByRoleIds(List.of(roleId)).stream().map(SysMenuTreeVo::getId).collect(Collectors.toSet());
                    if (resourceIds.isEmpty()) {
                        return;
                    }
                    resourceService.getResourceUrlMapping(resourceIds).stream().filter(mapping -> StringUtils.equals(url, mapping.getUrl()))
                            .forEach(mapping -> {
                                roleForDataPermPros.addAll(sysRoleDataPermProMapper.selectList(new LambdaQueryWrapper<SysRoleDataPermPro>()
                                        .eq(SysRoleDataPermPro::getRoleId, roleId)
                                        .eq(SysRoleDataPermPro::getMenuId, mapping.getResourceId())));
                                matchedRoleIds.add(roleId);
                            });
                });

                roleForDataPermPros.stream().filter(o -> StringUtils.isNotEmpty(o.getDataPermConfig()))
                        .max((o1, o2) -> {
                            if (StringUtils.isNotEmpty(o1.getDataPermConfig()) && StringUtils.isNotEmpty(o2.getDataPermConfig())) {
                                Map<String, Object> data1 = JacksonUtil.json2Map(o1.getDataPermConfig());
                                Map<String, Object> data2 = JacksonUtil.json2Map(o2.getDataPermConfig());
                                return data1.size() - data2.size();
                            } else if (StringUtils.isNotEmpty(o1.getDataPermConfig())) {
                                return 1;
                            } else if (StringUtils.isNotEmpty(o2.getDataPermConfig())) {
                                return -1;
                            }
                            return 0;
                        }).ifPresent(roleForDataPermPro -> dataPerms.setDataPermProVal(JacksonUtil.json2Map(roleForDataPermPro.getDataPermConfig())));

                roleDataResourceMapper.selectList(new LambdaQueryWrapper<RoleDataResource>().in(RoleDataResource::getRoleId, matchedRoleIds))
                        .stream().min(Comparator.comparingInt(RoleDataResource::getDataType))
                        .ifPresent(rdr -> {
                            dataPerms.setDataPermScope(FormConstants.DataPermScope.of(rdr.getDataType()));
                            if (StringUtils.isNotEmpty(rdr.getOrgIds())) {
                                dataPerms.setScopeOrgIds(Arrays.stream(rdr.getOrgIds().split(",")).map(Long::parseLong).toList());
                            }
                        });
                return dataPerms;
            });
        } catch (ExecutionException e) {
            log.error("getDataPermsByFunPerm error", e);
        }
        return new DataPerms();
    }

    @Data
    public static class DataPerms {
        private Map<String, Object> dataPermProVal;
        private FormConstants.DataPermScope dataPermScope;
        private List<Long> scopeOrgIds;
    }
}
